using System;
using System.Collections;
using Iesi.Collections;

namespace Sedna.Util
{
	public sealed class Collections
	{
		private readonly static IList EMPTY_LIST_INSTANCE = new ReadOnlyList();
		private readonly static IDictionary EMPTY_DICTIONARY_INSTANCE = new ReadOnlyDictionary();
		private readonly static ISet EMPTY_SET_INSTANCE = new ImmutableSet(new ListSet());

		public static IList EMPTY_LIST
		{
			get { return EMPTY_LIST_INSTANCE;}
		}

		public static IDictionary EMPTY_DICTIONARY
		{
			get { return EMPTY_DICTIONARY_INSTANCE;}
		}

		public static ISet EMPTY_SET
		{
			get { return EMPTY_SET_INSTANCE;}
		}

        public static ICollection IndefiniteCollection(ICollection collection)
        {
            return new ReadOnlyCollection(collection);
        }
	    
	    public static IList ToReadOnlyList(ICollection collection)
	    {
            return new ReadOnlyList(collection);
	    }
	    
		public static IList ReadOnly(IList list)
		{
			return new ReadOnlyList(list);
		}

		public static IDictionary ReadOnly(IDictionary dictionary)
		{
			return new ReadOnlyDictionary(dictionary);
		}

		public static ISet ReadOnly(ISet set)
		{
			return new ImmutableSet(set);
		}

	    public static object[] ToArray(ICollection collection)
	    {
	        object[] arr = new object[collection.Count];
	        collection.CopyTo(arr, 0);
            return arr;
	    }
	    	    
		private Collections()
		{}				

		#region Readonly dictionary wrapper

		private class ReadOnlyDictionary : Hashtable
		{
		    public ReadOnlyDictionary() {}

            public ReadOnlyDictionary(IDictionary initial)
            {
                foreach (DictionaryEntry entry in initial)
                {
                    base.Add(entry.Key, entry.Value);
                }
            }

            public override object this[object key]
            {
                get { return base[key];}
                set { Add(key, value);}
            }
		    
			public override void Add(object key, object value)
            {
                throw new NotSupportedException("Add not supported by read-only hashtable");
            }

            public override void Remove(object key)
            {
                throw new NotSupportedException("Remove not supported by read-only hashtable");
            }

            public override void Clear()
            {
                throw new NotSupportedException("Clear not supported by read-only hashtable");
            }
		}

		#endregion

		#region Readonly list wrapper

		private class ReadOnlyList : ArrayList
		{
		    public ReadOnlyList() {}

            public ReadOnlyList(ICollection initial)
            {
                foreach (object o in initial)
                {
                    base.Add(o);
                }
            }

            public override object this[int index]
            {
                get { return base[index];}
                set { Insert(index, value);}
            }            
		    
            public override int Add(object value)
            {
                throw new NotSupportedException("Add not supported by read-only list");
            }

            public override void Insert(int index, object value)
            {
                throw new NotSupportedException("Insert not supported by read-only list");
            }
		    
		    public override void AddRange(ICollection c)
            {
                throw new NotSupportedException("AddRange not supported by read-only list");
            }

            public override void Remove(object obj)
            {
                throw new NotSupportedException("Remove not supported by read-only list");
            }

            public override void RemoveAt(int index)
            {
                throw new NotSupportedException("Remove not supported by read-only list");
            }

            public override void RemoveRange(int index, int count)
            {
                throw new NotSupportedException("Remove not supported by read-only list");
            }

            public override void Clear()
            {
                throw new NotSupportedException("Clear not supported by read-only list");
            }
		    
			public override bool IsReadOnly
			{
				get { return true;}
			}
		}

		#endregion		
	    
        #region Readonly collection wrapper
	    
	    private class ReadOnlyCollection : ICollection
	    {
            private IList list = new ArrayList();
	        
	        public ReadOnlyCollection()
	        {}
	        
	        public ReadOnlyCollection(ICollection collection)
	        {
	            foreach (object o in collection)
	            {
                    list.Add(o);
	            }
	        }
	        
	        public void CopyTo(Array array, int index)
	        {
	            list.CopyTo(array, index);
	        }

	        public int Count
	        {
                get { return list.Count;}
	        }

	        public object SyncRoot
	        {
                get { return list.SyncRoot;}
	        }

	        public bool IsSynchronized
	        {
                get { return list.IsSynchronized;}
	        }

	        public IEnumerator GetEnumerator()
	        {
                return list.GetEnumerator();
	        }
	    }
	    
        #endregion

        # region Operations

        public static bool IsEmpty(ICollection coll)
        {
            return (coll == null || coll.Count == 0);
        }

        public static IList AddAll(IList toColl, ICollection fromColl)
        {
            foreach (Object o in fromColl)
            {
                toColl.Add(o);
            }
            return toColl;
        }

        public static Boolean CompareCollections(ICollection c1, ICollection c2)
        {
            if (c1 == null || c2 == null) return false;
            if (c1.Count != c2.Count) return false;

            if ((c1 is IList) && (c2 is IList))
            {
                IList l1 = c1 as IList;
                IList l2 = c2 as IList;
                for (int i = 0; i < c1.Count; i++)
                    if (!l1[i].Equals(l2[i])) return false;
                return true;
            }
            else
            {
                ISet s1 = new HashedSet(c1);
                ISet s2 = new HashedSet(c2);
                return s1.ContainsAll(s2) && s2.ContainsAll(s1);
            }
        }

	    /// <summary>
	    /// Clones alements in collections
	    /// </summary>
        public static ICollection Clone(ICollection collection)
	    {
            if (collection is ISet)
            {
                ISet set = new ListSet();

                foreach (ICloneable el in collection)
                {
                    set.Add(el.Clone());
                }
                
                return set;
            }
            else if (collection is IList)
            {
                IList list = new ArrayList(collection.Count);

                foreach (ICloneable el in collection)
                {
                    list.Add(el.Clone());
                }

                return list;
            }

            throw new Exception(String.Format("Unknown collection type '{0}'", collection.GetType()));
	    }
	    
        # endregion
    }
}
